Connector Data Source

OneStream can connect and import data from any external system using direct database connection to the external system. This means data can be imported and processed all the way through the Workflow certification process without ever having to use a source system extract file. 

Connector Business Rule

A Connector Business Rule defines the connection, data result sets, and drill-back option capabilities of an external data connection. A Connector functions as a Business Rule called by a Data Source and reveals what information is required from an external system.  See Business Rules in Application Tools for an example of this rule.

Connector Data Source

Fields from the external data query results are mapped to Dimensions creating a processing behavior similar to the behavior of a Delimited File. Using this mapping process enables a Connector Data Source to use all the same built processing capabilities available with file-based Data Sources. This capability enables the design of an external data Connector to be entirely focused on connecting to and reading data from an external source instead of focusing on integrating complex business logic. The specific business logic can be added to the Data Source Dimensions in the form of a Complex Expression or Business Rule. This design methodology will help with writing the Connector Business Rule in a way that requires very little maintenance by business users.

Connector Information Request Types

GetFieldList

This is called by the Data Source designer screen when the user selects a Connector Data Source or one of its defined Dimensions. A list of available fields in the external Data Source will be visible as a list of Vb.Net Strings [List(Of String)] is requested.

GetData

This is called by the Import Workflow task when the Load and Transform button is clicked. The execution of a data query(s) that retrieves the row values for the chosen Workflow Unit is requested.

Fields

The field names returned by this query must match the field names returned by the GetFieldList request.

Where Clause

Typically the active Workflow Unit Time and Scenario values are converted to equivalent criteria values for the Time or Scenario of the external system.

Data Volume

Consider loading summarized data rather than full transaction system data replication because drill back is provided for more detailed values.

GetDrillBackTypes

Drill Back types can deliver results based on the different visualization types. This is called when a user double-clicks or right-clicks and selects Drill Back from a row in the source data load or drill down screens.  A set of supported drill-back options to present to the end user as a list of DrillBackTypeInfo objects [List(Of DrillBackTypeInfo)] is requested. Drill Back types provide the Connector designer with the power to provide the end user with a menu list of drill back options. 

DataGrid

This presents a grid of data rows to the end user.

TextMessage

This presents a text message to the end user.

WebUrl

This presents a website or custom HTML web content to the end user.

WebUrlPopOutDefaultBrowser

Opens a website or custom HTML web content in an external browser. From the Stage Import data grid, right-click on a data record, and select Drill Back. A dialog presents a menu of pre-configured Drill-back options. When you choose WebUrlPopOutDefaultBrowser, a standard browser session is launched, and you go to a web page based on variables.

FileViewer

This presents file contents to the end user from one of three locations.

FileShareFile

A file located in a folder in the OneStream File Share.

AppDBFile

A file stored in an application database.

SysDBFile

A file stored in a framework (System) database.

GetDrillBack

This is called when a user selects a specific Drill Back type presented by the GetDrillBackTypes request. When this action is executed, the Business Rule arguments will contain a reference to the DrillBackTypeInfo object the user selected which allows the Connector designer to determine how to get proper information to display for the DrillBackTypeInfo.

Connector Integration Prerequisites

The following items provide an overview of the major technology components involved in integrating external systems with deployment.

Determine Source System Inventory

The first step in integrating various source systems is to determine all the ones needed. This includes:

Source System Location & Identification

Database Type and Source System

Oracle, SQL, DB2, Syteline, Newstar, Lawson, PeopleSoft, Access, MAS500, etc.

NOTE: The requirement for Oracle Database integrations is that all Oracle Source System TNS Profile details need to be in place on each of the OneStream application servers.

Data Query Method

Detailed Data Query, Data View, Stored Procedure, etc.

Source System Drill Back Criteria (if required)

Detailed Data Query, Data View, Stored Procedure, etc.

Source System Direct Access Credentials

A read-only type of access needs to be granted for the user account because the data from these external systems will be read. The read-only access should be granted against the productions instance of the data source as the data queries will be used to tie out data and do not present any risk to the source system themselves.

Source System 64-bit Client Data Provider

OneStream is a Microsoft .NET application with a 64-bit architecture. To communicate with any source system, a 64-bit source system client data provider needs to be available and installed on each OneStream application server. The source system’s client data provider is what gives the ability to make an OLEDB or ODBC connection to the system.

Determine Connection String

A connection string specifies information about a data source and the means of connecting to it. It is passed in code to an underlying driver or provider to initiate the connection. Whilst commonly used for a database connection, the data source could also be a spreadsheet or text file. The connection string may include attributes such as the name of the driver, server and database, as well as security information such as user name and password.

Create a Connection String from the OneStream Application Server

  1. Right-click the Desktop icon of the OneStream application server and select New > Text Document.

  2. Name the document and change the file extension from txt to udl.

    This creates a Data Link File to assist in the formation of the source system connection string.

  3. Determine the DB Provider that the GL Source System is using (e.g. SQL, Oracle, etc.).

  4. Determine the server name where the data resides for the GL Source System.

  5. Determine the user name and password used to connect to the server for the GL Source System.

  6. Determine the database name on the server where the GL Source System data resides.

  7. Save the completed UDL file and then rename the extension back to txt from udl.

  1. Open the text file to see the connection string provided.

Example Connection Strings

SQL Server

Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=DBName;Data Source=SQLSERVERNAME

ORACLE (11i or R12)

Provider=OraOLEDB.Oracle.1;Password=<xxxxx>;Persist Security Info=True;User ID=<username>;Data Source=frepro.world

DB2

Provider=IBMDA400.DataSource.1;Password=<xxxxx>;Persist Security Info=True;User ID=OSuser;Data Source=HUTCH400;Use SQL Packages=True

MS Access

Provider=Microsoft.ACE.OLEDB.12.0;Data Source=\\UNCFileShare\DB1.accdb;Mode=Read|Share Deny None;Persist Security Info=False

Determine the Data Query Method

To extract data from any source system, the data query method and facility need to be determined. Data can be queried through a SQL Query, a SQL View, or Stored Procedure. OneStream executes this request against the source system using the defined source system connection string and processes the returned results within OneStream.

For example, if directly pulling in Trial Balance Data is required, then the detailed query that currently makes up the existing Trial Balance Report would be necessary for OneStream to pull the same data.

SQL Query
A SQL Query can be broken down into numerous elements, each beginning with a keyword. Although it is not necessary, a common convention is to write these keywords in all capital letters.
The standard sections of a SQL Query are made up of the following four elements:

SELECT
FROM
WHERE
ORDER BY

The example below is a SQL Query used to pull Trial Balance Data from several different tables in an Oracle Database:

SELECT 
GL_SETS_OF_BOOKS.NAME
,GL_BALANCES.ACTUAL_FLAG
,GL_BALANCES.PERIOD_NAME
,GL_BALANCES.PERIOD_NUM
,GL_BALANCES.PERIOD_YEAR
,GL_CODE_COMBINATIONS.CODE_COMBINATION_ID
,GL_CODE_COMBINATIONS.SEGMENT1
,GL_CODE_COMBINATIONS.SEGMENT2
,GL_CODE_COMBINATIONS.SEGMENT3
,GL_CODE_COMBINATIONS.SEGMENT4
,GL_CODE_COMBINATIONS.SEGMENT5
,GL_CODE_COMBINATIONS.SEGMENT6
,GL_CODE_COMBINATIONS.SEGMENT7
,GL_CODE_COMBINATIONS.SEGMENT8
,GL_CODE_COMBINATIONS.SEGMENT9
,GL_CODE_COMBINATIONS.SEGMENT10
,SUM( NVL(GL_BALANCES.BEGIN_BALANCE_DR,0) - NVL(GL_BALANCES.BEGIN_BALANCE_CR,0))"OPEN BAL"
,NVL(GL_BALANCES.PERIOD_NET_DR,0) "DEBIT"
,NVL(GL_BALANCES.PERIOD_NET_CR,0) "CREDIT"
,SUM( NVL(GL_BALANCES.PERIOD_NET_DR,0) - NVL(GL_BALANCES.PERIOD_NET_CR,0))"NET MOVEMENT"
,SUM(( NVL(GL_BALANCES.PERIOD_NET_DR,0) + NVL(GL_BALANCES.BEGIN_BALANCE_DR,0))) - SUM (NVL(GL_BALANCES.PERIOD_NET_CR,0)+NVL(GL_BALANCES.BEGIN_BALANCE_CR,0))"CLOSE BAL"
,GL_BALANCES.CURRENCY_CODE
,GL_BALANCES.TRANSLATED_FLAG
,GL_BALANCES.TEMPLATE_ID
,FND_FLEX_VALUES_VL.FLEX_VALUE ,FND_FLEX_VALUES_VL.DESCRIPTION
,FND_FLEX_VALUES_VL.FLEX_VALUE_SET_ID   FROM GL_BALANCES, GL_CODE_COMBINATIONS, GL_SETS_OF_BOOKS, FND_FLEX_VALUES_VL   WHERE GL_CODE_COMBINATIONS.CODE_COMBINATION_ID = GL_BALANCES.CODE_COMBINATION_ID AND GL_BALANCES.ACTUAL_FLAG = 'A' AND GL_BALANCES.CURRENCY_CODE = GL_SETS_OF_BOOKS.CURRENCY_CODE AND GL_BALANCES.LEDGER_ID = GL_SETS_OF_BOOKS.SET_OF_BOOKS_ID AND GL_BALANCES.TEMPLATE_ID IS NULL AND GL_BALANCES.PERIOD_NAME = 'Jul-14' AND FND_FLEX_VALUES_VL.FLEX_VALUE = GL_CODE_COMBINATIONS.SEGMENT4 AND FND_FLEX_VALUES_VL.FLEX_VALUE_SET_ID = '101432874' AND GL_CODE_COMBINATIONS.SEGMENT2 IN (2050, 2100, 2200, 2300, 2400, 2500)   GROUP BY  GL_SETS_OF_BOOKS.NAME ,GL_BALANCES.ACTUAL_FLAG ,GL_BALANCES.PERIOD_NAME ,GL_BALANCES.PERIOD_NUM ,GL_BALANCES.PERIOD_YEAR ,GL_CODE_COMBINATIONS.CODE_COMBINATION_ID ,GL_CODE_COMBINATIONS.SEGMENT1 ,GL_CODE_COMBINATIONS.SEGMENT2 ,GL_CODE_COMBINATIONS.SEGMENT3 ,GL_CODE_COMBINATIONS.SEGMENT4 ,GL_CODE_COMBINATIONS.SEGMENT5 ,GL_CODE_COMBINATIONS.SEGMENT6 ,GL_CODE_COMBINATIONS.SEGMENT7 ,GL_CODE_COMBINATIONS.SEGMENT8 ,GL_CODE_COMBINATIONS.SEGMENT9 ,GL_CODE_COMBINATIONS.SEGMENT10 ,NVL(GL_BALANCES.PERIOD_NET_DR,0) ,NVL(GL_BALANCES.PERIOD_NET_CR,0) ,GL_BALANCES.CURRENCY_CODE ,GL_BALANCES.TRANSLATED_FLAG ,GL_BALANCES.TEMPLATE_ID ,FND_FLEX_VALUES_VL.FLEX_VALUE ,FND_FLEX_VALUES_VL.DESCRIPTION ,FND_FLEX_VALUES_VL.FLEX_VALUE_SET_ID HAVING SUM(( NVL(GL_BALANCES.PERIOD_NET_DR,0) + NVL(GL_BALANCES.BEGIN_BALANCE_DR,0))) - SUM(NVL(GL_BALANCES.PERIOD_NET_CR,0)+NVL(GL_BALANCES.BEGIN_BALANCE_CR,0)) <> 0

SQL View

In many cases, creating a SQL View of data to provide information to OneStream is a more preferred option and typically simplifies the complexity of the query.

In the example below, the customer can combine several data tables required in the source system, and present the data in one View for OneStream to query:

SELECT
SEGMENT1 As Entity
SEGMENT2 As Establishment
SEGMENT3 As France_Account
SEGMENT4 As US_Account
SEGMENT5 As Cost_Center
SEGMENT6 As Family
SEGMENT7 As Product_Line
SEGMENT8 As Interco
SEGMENT9 As Future
PERIOD_YEAR As Year
PERIOD_MONTH As Month
CURRENCY_CODE As Currency_Code
CLOSE_NET_BALANCE As Net_Balance
SET_OF_BOOKS_ID As Set_Of_Books_ID    
FROM APPS.XXSWM_ONESTREAM_GL_BALANCES

Stored Procedure

The example below is a SQL Stored Procedure used to pull Trial Balance Data from several different tables in a SQL Database. 

In this example, the Entity, Year, and Period are passed to the Stored Procedure:

spGLCalcTrialBalance 'ASCC', '2013', 6

Apply Connection String to XFAppServerConfig.xml File

When the connection string is created, then the database connections can be centralized in the Server Configuration under the App Server Configuration File. Under Databases, click on (Collection) for Database Server Connections and the Database Server Connections will appear. The string will then be placed in the Connection String under Connection String Settings. The name of the connection string will be used as part of the source connector. 

Defining External Data Connections

Application Server Configuration File
Creating Named External ODBC / OLEDB Connection

Step 1: Required ODBC/OLEDB Connection Software
Any client ODBC/OLEDB drivers must be installed on each application server for the OneStream application to make a connection to the external database. This way the administrator knows what type of database engine contains which Data Source.

Step 2: Creating the Connection String
The application server configuration file must be modified to add a named external database connection that can be used by the Connector Business Rule and custom reports.

Example of the Server Configuration Utility:

Step 3: Creating an External Database Test Query
The best way to prototype the queries needed to create a Connector Business Rule is to create a set of Dashboard Data Adapters to be used as a test bed.

As a best practice, create a new Dashboard Maintenance Unit named EXS The Connector Name. The prefix EXS stands for External System and will provide administrators with an immediate understanding of the Maintenance Unit’s contents. The three steps below explain how to create this.

Step 1
Create a new Data Adapter for each type of query needed to proto type (GetFieldList, SelectData, Drill Back, etc.)

Example of a Data Adapter being used to get all fields in the source table of the external database connection:

Step 2
Click in the Dashboard administration toolbar to test the query.

Step 3
Evaluate the results of the query. The Data Adapter test only returns a small subset of rows from the query, but it specifies the actual number of rows that will return during an actual query execution.

Building Data Connectors

ODBC / OLEDB Connectors

GetFieldList

Select Query against the external database. There will be a manual list of strings returned for each field.

GetData

The selected statement should match GetFieldList. Add criteria for Scenario and Time and map the OneStream Workflow Unit Scenario and Time values to corresponding values in the source system as a Where Clause criteria value.

GetDrillBackTypes

This shows the set of drill back options provided to the user.

GetDrillBack

This executes the selected drill back type for the current source data row.

Custom API Connectors

Uses OneStream's External Server Technology. IIS needs to be recycled on all application servers and followed by all web servers after adding the external named connection.

Drill Back

Using a SQL connector allows a user to drill back to a source system and show detailed records from a document, PDF, website. The Connector Data Source, configured by the author, provides a menu of data viewing options such as Year to Date, Month to Date, Invoice Documents or Material Type Detail. Utilizing this feature can reduce the amount of data imported into the Financial Model by allowing analysis to occur at the source system. 

Viewing Data

After data is loaded into the Stage, a user can right-click on a data row and select Drill Back. This will bring up the pre-configured options from which the user can choose.

If more detail is needed, another level of Drill Back can be performed. This is configured in the Connector Business Rule and can drill back and around source systems. These nested drill paths can provide as much detail as an application requires.

Key API, Args, or BRAPI Examples

Namespace OneStream.BusinessRule.Connector.RevenueMgmtHouston
    Public Class MainClass
        Public Function Main(ByVal si As SessionInfo, ByVal globals As BRGlobals, 
ByVal api As Transformer, ByVal args As ConnectorArgs) As Object
            Try
                'Get the query information
                Dim connectionString As String = GetConnectionString(si, globals, api)
                                                
                'Get the Field name list or load the data    
                Select Case args.ActionType
                    Case Is = ConnectorActionTypes.GetFieldList
                        'Return Field Name List
                        Dim fieldListSQL As String = GetFieldListSQL(si, globals, api)
                        Return api.Parser.GetFieldNameListForSQLQuery(si, DbProviderType.OLEDB,
 connectionString, true, fieldListSQL, false)
                        
                    Case Is = ConnectorActionTypes.GetData
                        'Process Data
                        Dim sourceDataSQL As String = GetSourceDataSQL(si, globals, api)
                        api.Parser.ProcessSQLQuery(si, DbProviderType.OLEDB, connectionString, true, 
				sourceDataSQL, false, api.ProcessInfo)
                        Return Nothing
 
                    Case is = ConnectorActionTypes.GetDrillBackTypes
                        'Return the list of Drill Types (Options) to present to the end user
                        Return  Me.GetDrillBackTypeList(si, globals, api, args)
 
                    Case Is = ConnectorActionTypes.GetDrillBack
                        'Process the specific Drill-Back type
                        Return Me.GetDrillBack(si, globals, api, args, args.DrillBackType.DisplayType, 
				connectionString)
                End Select
                                
                Catch ex As Exception
                Throw ErrorHandler.LogWrite(si, New XFException(si, ex))
            End Try
        End Function                    
        
        'Create a Connection string to the External Database
        Private Function GetConnectionString(ByVal si As SessionInfo, ByVal globals As BRGlobals, 
		ByVal api As Transformer) As String
            Try
                                
                'Connection String Method
                '-----------------------------------------------------------
'                Dim connection As New Text.StringBuilder'                
'                connection.Append("Provider=SQLOLEDB.1;")
'                connection.Append("Data Source=LocalHost\MSSQLSERVER2008;")
'                connection.Append("Initial Catalog=SampleData;")
'                connection.Append("Integrated Security=SSPI")                
'                Return connection.ToString
                
                'Named External Connection
                '-----------------------------------------------------------
                Return "Revenue Mgmt System"
                
                Catch ex As Exception
                Throw ErrorHandler.LogWrite(si, New XFException(si, ex))
            End Try            
        End Function    
 
        'Create the field list SQL Statement
        Private Function GetFieldListSQL(ByVal si As SessionInfo, ByVal globals As BRGlobals, 
		ByVal api As Transformer) As String
            Try
                'Create the SQL Statement
                Dim sql As New Text.StringBuilder
                
                sql.Append("SELECT Top(1)") 
                sql.Append("TransID, PlantCode, CustId, CustName, InvNo,
                InvYear, InvMonth, InvDesc, GLAccount, WorkDay, ProdModel, BomCode, UnitPrice, Units, 
			Amount, DestinationCode ")                
                sql.Append("FROM InvoiceDocumentDetail ")
                
                Return sql.ToString
                
                Catch ex As Exception
                Throw ErrorHandler.LogWrite(si, New XFException(si, ex))
            End Try            
        End Function
 
        'Create the data load SQL Statement
        Private Function GetSourceDataSQL(ByVal si As SessionInfo, ByVal globals As BRGlobals, 
		ByVal api As Transformer) As String
            Try
                'Create the SQL Statement
                Dim statement As New Text.StringBuilder
                Dim selectClause As New Text.StringBuilder
                Dim fromClause as New Text.StringBuilder
                Dim whereClause as New Text.StringBuilder
                Dim orderByClause as New Text.StringBuilder
                                
                selectClause.Append("SELECT ") 
                selectClause.Append("TransID, PlantCode, CustId, CustName,
 InvNo, InvYear, InvMonth, InvDesc, GLAccount, WorkDay, ProdModel, BomCode, UnitPrice,Units, Amount, 
			DestinationCode ")
                
                fromClause.Append("FROM InvoiceDocumentDetail ")
                
                whereClause.Append("WHERE ")                
                'Get the YEAR from the current XF Workflow Unit TimeKey
                whereClause.Append("(")
                whereClause.Append("InvYear = " & TimeDimHelper.GetYearFromId(api.WorkflowUnitPk.TimeKey).ToString)
                whereClause.Append(")")
                
                'Get the MONTH from the current XF Workflow Unit TimeKey
                whereClause.Append(" And ")
                whereClause.Append("(")
                whereClause.Append("InvMonth = 'M" & TimeDimHelper.GetSubComponentsFromId
			(api.WorkflowUnitPk.TimeKey)
                .Month.ToString & "'")                 whereClause.Append(") ")                   'Select Houston Plant Codes                 whereClause.Append(" And ")                 whereClause.Append("(")                 whereClause.Append("PlantCode IN('H200','H210')")                 whereClause.Append(") ")                   orderByClause.Append("ORDER BY ")                 orderByClause.Append("PlantCode, CustId, WorkDay, ProdModel, DestinationCode")                                                  'Create the full SQL Statement                 statement.Append(selectClause.ToString)                 statement.Append(fromClause.ToString)                 statement.Append(whereClause.ToString)                 statement.Append(orderByClause.ToString)                                  Return statement.ToString                              Catch ex As Exception                 Throw ErrorHandler.LogWrite(si, New XFException(si, ex))             End Try                     End Function           'Create the drill back options list         Private Function GetDrillBackTypeList(ByVal si As SessionInfo,
 ByVal globals As BRGlobals, ByVal api As Transformer, ByVal args As ConnectorArgs) As List(Of DrillBackTypeInfo)
            Try
                'Create the SQL Statement
                Dim drillTypes As New List(Of DrillBackTypeInfo)
                
                drillTypes.Add(New DrillBackTypeInfo(ConnectorDrillBackDisplayTypes.FileShareFile,
 New NameAndDesc("Invoice Document","Invoice Document")))
                drillTypes.Add(New DrillBackTypeInfo(ConnectorDrillBackDisplayTypes.DataGrid,
 New  NameAndDesc("Material Type Detail","Material Type Detail")))
                        
                Return drillTypes
                
                Catch ex As Exception
                Throw ErrorHandler.LogWrite(si, New XFException(si, ex))
            End Try            
        End Function
        
        'Execute specific drill back type
        private Function GetDrillBack(ByVal si As SessionInfo, ByVal globals As BRGlobals,
 ByVal api As Transformer,
 ByVal args As ConnectorArgs, ByVal drillBackType As ConnectorDrillBackDisplayTypes, 
ByVal connectionString as String) As DrillBackResultInfo
            Try
                Select case drillBackType 
                    case is = ConnectorDrillBackDisplayTypes.FileShareFile
                        'Show FileShare File
                        Dim drillBackInfo as new DrillBackResultInfo
                        drillBackInfo.DisplayType = ConnectorDrillBackDisplayTypes.FileShareFile
                        drillBackInfo.DocumentPath = Me.GetDrillBackDocPath(si, globals, api, args)                        
                        return drillBackInfo
 
                    case is = ConnectorDrillBackDisplayTypes.DataGrid                                                    
                        'Return Drill Back Detail
                        Dim drillBackSQL As String = GetDrillBackSQL(si, globals, api, args)
                        Dim drillBackInfo as new DrillBackResultInfo
                        drillBackInfo.DisplayType = ConnectorDrillBackDisplayTypes.DataGrid                                
                        drillBackInfo.DataTable = api.Parser.GetXFDataTableForSQLQuery(si,  
                        DbProviderType.OLEDB, connectionString, true, drillBackSQL,
 false, args.PageSize, args.PageNumber)
                        return drillBackInfo
                                            
                        case else
                        return Nothing    
                End Select    
                        
                Catch ex As Exception
                Throw ErrorHandler.LogWrite(si, New XFException(si, ex))
            End Try    
        End Function
 
        'Create the drill back Document Path
        Private Function GetDrillBackDocPath(ByVal si As SessionInfo, 
ByVal globals As BRGlobals,ByVal api As Transformer, ByVal args As ConnectorArgs) As String
            Try
                'Get the values for the source row that we are drilling back to
                Dim sourceValues as Dictionary(Of string, Object) =  
api.Parser.GetFieldValuesForSourceDataRow(si, args.RowID)
                If (Not sourceValues Is Nothing) And (sourceValues.Count > 0) then                    
                    Return "Applications/GolfStream_v24/DataManagement/RevenueMgmtInvoices/"
 & sourceValues.Item(StageConstants.MasterDimensionNames.Attribute1).ToString & ".pdf"
                Else
                    Return String.Empty
                End If
                Catch ex As Exception
                Throw ErrorHandler.LogWrite(si, New XFException(si, ex))
            End Try            
        End Function
 
    'Create the drill back SQL Statement
        Private Function GetDrillBackSQL(ByVal si As SessionInfo, ByVal globals As BRGlobals,
 ByVal api As Transformer, ByVal args As ConnectorArgs) As String
            Try
                'Get the values for the source row that we are drilling back to
                Dim sourceValues as Dictionary(Of string, Object) =  
api.Parser.GetFieldValuesForSourceDataRow(si, args.RowID)
                If (Not sourceValues Is Nothing) And (sourceValues.Count > 0) then                
 
                    Dim statement As New Text.StringBuilder
                    Dim selectClause As New Text.StringBuilder
                    Dim fromClause as New Text.StringBuilder
                    Dim whereClause as New Text.StringBuilder
                    Dim orderByClause as New Text.StringBuilder
 
                    'Create the SQL Statement                    
                    selectClause.Append("SELECT ") 
                    selectClause.Append("CustName, InvDesc, BomCode, UnitPrice, Units,Amount ")
                    
                    fromClause.Append("FROM InvoiceMaterialDetail ")
                    
                    whereClause.Append("WHERE ")                
                    'Get the YEAR from the source record
                    whereClause.Append("(")
                    whereClause.Append("InvYear = " &  TimeDimHelper.GetYearFromId(sourceValues.Item
                    (StageTableFields.StageSourceData.DimWorkflowTimeKey).ToString))                     whereClause.Append(")")                                          'Get the MONTH from the source record                     whereClause.Append(" And ")                     whereClause.Append("(")                     whereClause.Append("InvMonth = 'M" & TimeDimHelper.GetSubComponentsFromId(sourceValues.Item
                    (StageTableFields.StageSourceData.DimWorkflowTimeKey))
                    .Month.ToString & "'")                     whereClause.Append(")")                       whereClause.Append(" And ")                     whereClause.Append("(")                     whereClause.Append("PlantCode = '" & sourceValues.Item (StageConstants.MasterDimensionNames.Entity)
                    .ToString & "'")                     whereClause.Append(")")                       whereClause.Append(" And ")                     whereClause.Append("(")                     whereClause.Append("InvNo = '" & sourceValues.Item (StageConstants.MasterDimensionNames.Attribute1)
                    .ToString & "'")                     whereClause.Append(")")                                          whereClause.Append(" And ")                     whereClause.Append("(")                     whereClause.Append("ProdModel = '" & sourceValues.Item (StageConstants.MasterDimensionNames.UD2).ToString& "'")                     whereClause.Append(")")                       whereClause.Append(" And ")                     whereClause.Append("(")                     whereClause.Append("DestinationCode = '" & sourceValues.Item (StageConstants.MasterDimensionNames.UD3).
                    ToString & "'")                     whereClause.Append(")")                       whereClause.Append(" And ")                     whereClause.Append("(")                     whereClause.Append("CustID = '" &sourceValues.Item(StageConstants.MasterDimensionNames.UD4)
                    .ToString & "'")                     whereClause.Append(")")                                          orderByClause.Append("ORDER BY ")                     orderByClause.Append("BomCode")                                          'Create the full SQL Statement                     statement.Append(selectClause.ToString)                     statement.Append(fromClause.ToString)                     If args.ClientFilterRequest.length > 0 then                         statement.Append(whereClause.ToString)                         statement.Append(" And ")                         statement.Append(args.ClientFilterRequest)                     Else                         statement.Append(whereClause.ToString)                     End If                                         If args.ClientSortRequest.Length > 0 then                         statement.Append(args.ClientSortRequest)                     Else                         statement.Append(orderByClause.ToString)                     End if                     'ErrorHandler.LogMessage(si, statement.ToString)                     Return statement.ToString                 Else                     Return String.Empty                 End If             Catch ex As Exception                 Throw ErrorHandler.LogWrite(si, New XFException(si, ex))             End Try                     End Function     End Class End Namespace